package com.lzw.app;

import com.lzw.main.PingPongThread;
import com.lzw.main.ShowThread;
import com.lzw.property.LzwProperties;
import com.lzw.property.ServerProperties;

import java.io.*;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author LZW
 */
@SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
public class ClientApp {
    private InstanceRepository instanceRepository;
    //注册中心应用的状态：true表示开启
    public static boolean status;
    //服务端口+ip
    private static String ip_self;
    private static int port_self;
    private static ThreadPoolExecutor taskThreadPool;
    //单例模式——懒汉式
    private static ClientApp clientApp;

    //静态代码块，初始化线程池
    static {
        //初始大小
        int initSizeTask = 10;
        //最大容量
        int maxSizeTask = 15;
        //存活时间
        int alivesecondTask = 60;

        //任务线程
        taskThreadPool = new ThreadPoolExecutor(initSizeTask, maxSizeTask, alivesecondTask, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            private int id = 0;

            public Thread newThread(Runnable r) {
                Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "任务线程-" + id++);
                if (t.isDaemon()) {
                    t.setDaemon(false);
                }
                if (t.getPriority() != Thread.NORM_PRIORITY) {
                    t.setPriority(Thread.NORM_PRIORITY);
                }
                return t;
            }
        });
        //预先开启核心部分线程
        taskThreadPool.prestartAllCoreThreads();
    }

    public InstanceRepository getInstanceRepository() {
        return instanceRepository;
    }

    public void setInstanceRepository(InstanceRepository instanceRepository) {
        this.instanceRepository = instanceRepository;
    }

    //构造方法私有化
    private ClientApp(String ip, int port) {
        ip_self = ip;
        port_self = port;
    }

    /**
     * 获取实例，并初始化注册中心
     */
    public static synchronized ClientApp getInstance(LzwProperties lzwProperties, ServerProperties serverProperties, InstanceRepository instanceRepository) throws IOException {
        String ip = serverProperties.getIp();
        int port = serverProperties.getPort();
        if (clientApp == null) {
            clientApp = new ClientApp(ip, port);
        }
        clientApp.setInstanceRepository(instanceRepository);
        //执行注册
        registerServers(lzwProperties, taskThreadPool);
        //执行第一次抓取
        firstFetch(lzwProperties, serverProperties, clientApp);
        //开启线程循环定时执行fetch
        if (lzwProperties.getFetchInterval() != 0) {
            startFetch(lzwProperties, serverProperties);
        }
        //开启线程循环执行pingPong
        startPingPong(lzwProperties, serverProperties);
        //展示
        showService();
        //返回app对象
        return clientApp;
    }

    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    private static void firstFetch(LzwProperties lzwProperties, ServerProperties serverProperties, ClientApp clientApp) {
        int fetchInterval = lzwProperties.getFetchInterval();
        List<String> serverIpPort = lzwProperties.getServerIpPort();
        if (serverIpPort == null || serverIpPort.size() == 0) {
            throw new RuntimeException("找不到注册中心");
        }
        boolean flag = true;
        while (flag) {
            int index = 0;
            while (index < serverIpPort.size()) {
                String ipPort = serverIpPort.get(index);
                try {
                    String[] split = ipPort.split(":");
                    String ip = split[0];
                    int port = Integer.parseInt(split[1]);
                    Socket socket = new Socket(ip, port);
                    OutputStream outputStream = socket.getOutputStream();
                    InputStream inputStream = socket.getInputStream();
                    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                    //传递action信息
                    bw.write("fetch");
                    bw.newLine();
                    bw.flush();
                    //接收response信息
                    String status = br.readLine();
                    if ("OK".equals(status)) {
                        ObjectOutputStream oos = new ObjectOutputStream(outputStream);
                        Instance instance = new Instance();
                        instance.setHost(ip_self);
                        instance.setPort(port_self);
                        String appName = lzwProperties.getAppName();
                        if (appName == null || "".equals(appName)) {
                            throw new RuntimeException("找不到服务名");
                        }
                        instance.setName(appName);
                        instance.setUrl("http://" + instance.getHost() + ":" + instance.getPort());
                        instance.setId(instance.getHost() + ":" + instance.getName() + ":" + instance.getPort());
                        instance.setInvalidateSecond(lzwProperties.getInvalidateTime());
                        oos.writeObject(instance);
                        oos.flush();
                    }
                    //接收订阅
                    ObjectInputStream ois = new ObjectInputStream(inputStream);
                    InstanceRepository instanceRepository = (InstanceRepository) ois.readObject();
                    //出现设置
                    clientApp.setInstanceRepository(instanceRepository);
                    socket.close();
                    flag = false;
                    break;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                index++;
            }
        }
    }


    private static void showService() {
        taskThreadPool.execute(new ShowThread(clientApp));
    }

    private static void startPingPong(LzwProperties lzwProperties, ServerProperties serverProperties) {
        taskThreadPool.execute(new PingPongThread(lzwProperties, serverProperties, taskThreadPool));
    }

    //获取服务
    private static void startFetch(final LzwProperties lzwProperties, ServerProperties serverProperties) {
        taskThreadPool.execute(new Runnable() {
            @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
            public void run() {

                int fetchInterval = lzwProperties.getFetchInterval();
                List<String> serverIpPort = lzwProperties.getServerIpPort();
                if (serverIpPort == null || serverIpPort.size() == 0) {
                    return;
                }
                while (true) {
                    //休眠
                    try {
                        Thread.sleep(fetchInterval * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //开始抓取
                    int index = 0;
                    while (index < serverIpPort.size()) {
                        String ipPort = serverIpPort.get(index);
                        try {
                            String[] split = ipPort.split(":");
                            String ip = split[0];
                            int port = Integer.parseInt(split[1]);
                            Socket socket = new Socket(ip, port);
                            OutputStream outputStream = socket.getOutputStream();
                            InputStream inputStream = socket.getInputStream();
                            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
                            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                            //传递action信息
                            bw.write("fetch");
                            bw.newLine();
                            bw.flush();
                            //接收response信息
                            String status = br.readLine();
                            if ("OK".equals(status)) {
                                ObjectOutputStream oos = new ObjectOutputStream(outputStream);
                                Instance instance = new Instance();
                                instance.setHost(ip_self);
                                instance.setPort(port_self);
                                String appName = lzwProperties.getAppName();
                                if (appName == null || "".equals(appName)) {
                                    return;
                                }
                                instance.setName(appName);
                                instance.setUrl("http://" + instance.getHost() + ":" + instance.getPort());
                                instance.setId(instance.getHost() + ":" + instance.getName() + ":" + instance.getPort());
                                instance.setInvalidateSecond(lzwProperties.getInvalidateTime());
                                oos.writeObject(instance);
                                oos.flush();
                            }
                            //接收订阅
                            ObjectInputStream ois = new ObjectInputStream(inputStream);
                            InstanceRepository instanceRepository = (InstanceRepository) ois.readObject();
                            //出现设置
                            clientApp.setInstanceRepository(instanceRepository);
                            String resp = br.readLine();
                            socket.close();
                            break;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        index++;
                    }
                }
            }
        });
    }

    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    private static synchronized void registerServers(final LzwProperties lzwProperties, ThreadPoolExecutor poolExecutor) {
        List<String> serverIpPort = lzwProperties.getServerIpPort();
        if (serverIpPort == null || serverIpPort.size() == 0) {
            return;
        }
        for (final String ipPort : serverIpPort) {
            poolExecutor.execute(new Runnable() {
                public void run() {
                    registerServer(ipPort, lzwProperties);
                }
            });
        }
    }

    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    private static synchronized void registerServer(String ipPort, LzwProperties lzwProperties) {
        int invalidateTime = lzwProperties.getInvalidateTime();
        if (ipPort == null || invalidateTime == 0) {
            System.err.println("url或失效时间不正常");
            return;
        }
        try {
            String[] split = ipPort.split(":");
            String ip = split[0];
            int port = Integer.parseInt(split[1]);
            Socket socket = new Socket(ip, port);
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            //传递action信息
            bw.write("add");
            bw.newLine();
            bw.flush();
            //接收response信息
            String status = br.readLine();
            if ("OK".equals(status)) {
                ObjectOutputStream oos = new ObjectOutputStream(outputStream);
                Instance instance = new Instance();
                instance.setHost(ip_self);
                instance.setPort(port_self);
                String appName = lzwProperties.getAppName();
                instance.setName(appName);
                instance.setUrl("http://" + instance.getHost() + ":" + instance.getPort());
                instance.setId(instance.getHost() + ":" + instance.getName() + ":" + instance.getPort());
                instance.setInvalidateSecond(invalidateTime);
                oos.writeObject(instance);
                oos.flush();
            }
            //接收反馈
            String resp = br.readLine();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 轮询获取服务
     *
     * @param serviceName 服务名
     * @return 服务ip:port
     */
    public String getServiceByNameOrder(String serviceName) {
        Instance instance = this.instanceRepository.getInstanceByNameOrder(serviceName);
        return instance.getUrl();
    }

    public String getServiceByNameRandom(String serviceName) {
        Instance instance = this.instanceRepository.getInstanceByNameRandom(serviceName);
        return instance.getUrl();
    }
}
